#include "session.h"
#include <string>
#include <functional>
#include "nlohmann/json.hpp"
#include "arrow/log.h"
#include "session_machine/state_machin_define.h"
#include "session.h"
#include "session_manager.h"

// #include "../msg/msg_all_msg_src_info.h"

namespace ArrowWebCodeView
{
CSession::CSession(asio::io_context& ioContext, tcp::socket socket, const std::string& strConnectInfo)
    : m_IoContext(ioContext), m_WebSocket(std::move(socket)), m_strConnectInfo(strConnectInfo)
{
    ARROW_LOG_INFO_FMT("CSession client %s connected write buffer size:%zu", m_strConnectInfo.c_str(), m_WebSocket.write_buffer_bytes());
    m_WebSocket.write_buffer_bytes(4*1024*1024);

    m_MapStroToSessisonMachineMsg["Login"] = SessionMachine::MsgEnum::Login;
    m_MapStroToSessisonMachineMsg["Path"] = SessionMachine::MsgEnum::Path;
    m_MapStroToSessisonMachineMsg["Folder"] = SessionMachine::MsgEnum::Folder;
    m_MapStroToSessisonMachineMsg["File"] = SessionMachine::MsgEnum::File;

    SessionMachine::Data data;
    data.strDefaultPath = "/home/zhuyb/Documents/1.code/web_code_view";
    data.strPassword = "123456";
    m_SessionMachine.Init(data, SessionMachine::StateEnum::CheckPassword);
}

CSession::~CSession()
{
    ARROW_LOG_INFO_FMT("~CSession client %s disconnected", m_strConnectInfo.c_str());
}

void CSession::Start()
{
    m_WebSocket.async_accept(beast::bind_front_handler(&CSession::OnAccept, shared_from_this()));
}

void CSession::OnAccept(boost::system::error_code errorCode)
{
    if (errorCode)
    {
        ARROW_LOG_ERROR_FMT("%s accept error:%s", m_strConnectInfo.c_str(), errorCode.message().c_str());
        AppSessionManger::Instance()->RemoveSession(shared_from_this());
        return;
    }
    AppSessionManger::Instance()->AddSession(shared_from_this());
    DoReadCmd();
}

void CSession::DoReadCmd()
{
    m_WebSocket.async_read(m_ReadBuffer,beast::bind_front_handler(&CSession::OnReadCmd, shared_from_this()));
}

void CSession::OnReadCmd(boost::system::error_code errorCode, std::size_t bytesTransferred)
{
    if (errorCode)
    {
        ARROW_LOG_ERROR_FMT("%s read error:%s",m_strConnectInfo.c_str(), errorCode.message().c_str());
        AppSessionManger::Instance()->RemoveSession(shared_from_this());
        return;
    }

    std::string strMsg = beast::buffers_to_string(m_ReadBuffer.data());
    m_ReadBuffer.consume(m_ReadBuffer.size());

    DoReadCmdData(strMsg);
}


void CSession::DoReadCmdData(std::string strCmd)
{
    m_WebSocket.async_read(m_ReadBuffer,beast::bind_front_handler(&CSession::OnReadCmdData, shared_from_this(), strCmd));
}

void CSession::OnReadCmdData(std::string strCmd, boost::system::error_code errorCode, std::size_t bytesTransferred)
{
    if (errorCode)
    {
        ARROW_LOG_ERROR_FMT("%s read error:%s",m_strConnectInfo.c_str(), errorCode.message().c_str());
        AppSessionManger::Instance()->RemoveSession(shared_from_this());
        return;
    }

    std::string strInfo = beast::buffers_to_string(m_ReadBuffer.data());
    m_ReadBuffer.consume(m_ReadBuffer.size());
    std::string strOut;
    auto it = m_MapStroToSessisonMachineMsg.find(strCmd);
    if(it == m_MapStroToSessisonMachineMsg.end())
    {
        ARROW_LOG_ERROR_FMT("unknown cmd:%s", strCmd.c_str());
        DoReadCmd();
        return;
    }
    ARROW_LOG_INFO_FMT("cmd:%s ms:%s", strCmd.c_str(), strInfo.c_str());
    m_SessionMachine.HandleMsg(it->second, strInfo, strOut);
    
    if(strOut.empty())
    {
        DoReadCmd();
        return;
    }

    DoWrite(strOut);
}

void CSession::DoWrite(const std::string& strData)
{
    // try
    // {
    //     m_WebSocket.write(asio::buffer(strData));
    // }
    // catch (boost::system::error_code errorCode)
    // {
    //     ARROW_LOG_ERROR_FMT("%s write error:%s", m_strConnectInfo.c_str(), errorCode.message().c_str());
    //     AppSessionManger::Instance()->RemoveSession(shared_from_this());
    //     return;
    // }
    // DoReadCmd();
    m_WebSocket.async_write(asio::buffer(strData), beast::bind_front_handler(&CSession::OnWrite1, shared_from_this()));
}

void CSession::OnWrite1(boost::system::error_code errorCode, std::size_t bytesTransferred)
{
    ARROW_LOG_DEBUG_FMT("%s write %zu bytes",m_strConnectInfo.c_str(), bytesTransferred);
    if(errorCode)
    {
        ARROW_LOG_ERROR_FMT("%s write error:%s",m_strConnectInfo.c_str(), errorCode.message().c_str());
        AppSessionManger::Instance()->RemoveSession(shared_from_this());
        return;
    }
    DoReadCmd();
}

void CSession::OnWrite2(const std::string& strData, boost::system::error_code errorCode, std::size_t bytesTransferred)
{
    ARROW_LOG_DEBUG_FMT("%s write %zu bytes",m_strConnectInfo.c_str(), bytesTransferred);
    if(errorCode)
    {
        ARROW_LOG_ERROR_FMT("%s write error:%s",m_strConnectInfo.c_str(), errorCode.message().c_str());
        AppSessionManger::Instance()->RemoveSession(shared_from_this());
        return;
    }
    DoWrite(strData);
}


void CSession::Close()
{
    m_WebSocket.close(beast::websocket::close_code::normal);
    AppSessionManger::Instance()->RemoveSession(shared_from_this());
}

} // namespace ArrowWebCodeView